// 墨水屏屏幕库
#include "PDLS_EXT3_Basic_Global.h"

// SDK调用
#include "hV_HAL_Peripherals.h"
#include "hV_Configuration.h"

// 调用库
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <HTTPClient.h>

// JSON库
#include <ArduinoJson.h>

// 二维码库
#include "qrcode.h"

// API地址
#define API_URL "http://你的域名:8889/api/v1.0/product/info"
#define API_URL_HOST "你的域名对应的IP"
#define API_URL_PORT 8889
#define API_URL_PATH "/api/v1.0/product/info"

// 实例化屏幕类
Screen_EPD_EXT3 myScreen(eScreen_EPD_EXT3_370, boardRaspberryPiPico_RP2040);

// 网卡mac地址
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xE1, 0xF2, 0xE4 };

// 静态ip地址、DNS服务、网关、子网掩码
IPAddress ip(192, 168, 1, 238);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

// 变量定义
uint32_t interval = 300;  // 刷新间隔时间
uint32_t now = 0;         // 当前时间
bool refresh = false;     // 刷新标志


///
/// @brief 延时
/// @param second duration, s
///
void wait(uint8_t second) {
  for (uint8_t i = second; i > 0; i--) {
    Serial.print(formatString(" > %i  \r", i));
    delay(1000);
  }
  Serial.print("         \r");
}

///
/// @brief 字体
/// @param font_name
///
uint8_t get_font(String font_name) {
  if (font_name == "6x8") {
    return Font_Terminal6x8;
  } else if (font_name == "8x12") {
    return Font_Terminal8x12;
  } else if (font_name == "8x16") {
    return Font_Terminal8x16;
  } else if (font_name == "12x16") {
    return Font_Terminal12x16;
  } else if (font_name == "16x24") {
    return Font_Terminal16x24;
  } else {
    Serial.println(formatString("font_name is invalid: %s", font_name));
    return Font_Terminal6x8;
  }
}

///
/// @brief 颜色
/// @param color_name
///
uint16_t get_color(String color_name) {
  if (color_name == "black") {
    return myColours.black;
  } else if (color_name == "white") {
    return myColours.white;
  } else if (color_name == "grey") {
    return myColours.grey;
  } else if (color_name == "red") {
    return myColours.red;
  } else if (color_name == "lightRed") {
    return myColours.lightRed;
  } else if (color_name == "darkRed") {
    return myColours.darkRed;
  } else {
    Serial.println(formatString("color_name is invalid: %s", color_name));
    return myColours.black;
  }
}

///
/// @brief 绘制二维码
/// @param uint16_t x0 (aka unsigned short)
/// @param uint16_t y0 (aka unsigned short)
/// @param class arduino::String text
/// @param uint16_t textColour = myColours.black (aka unsigned short)
/// //@param uint16_t backColour = myColours.white (aka unsigned short)
///
void draw_qrcode(uint16_t x_offset, uint16_t y_offset, uint16_t w, uint16_t h, arduino::String text, uint16_t textColour) {
  // 开始时间
  uint32_t dt = millis();

  // 创建二维码
  QRCode qrcode;
  uint8_t qrcodeData[qrcode_getBufferSize(3)];
  qrcode_initText(&qrcode, qrcodeData, 3, 0, text.c_str());

  // 处理时间计算
  dt = millis() - dt;
  Serial.print("QR Code Generation Time: ");
  Serial.print(dt);
  Serial.print("\n");

  // 输出二维码信息
  Serial.print("\n\n\n\n");

  uint16_t backColour = get_color("white");
  for (uint8_t y = 0; y < qrcode.size; y++) {

    // 左则空区域
    Serial.print("        ");

    // 水平模块
    for (uint8_t x = 0; x < qrcode.size; x++) {
      bool p = qrcode_getModule(&qrcode, x, y);
      // 打印每个模块 (UTF-8 \u2588 is a solid block)
      Serial.print(p ? "\u2588\u2588" : "  ");

      // 打印每个模块
      uint16_t color = p ? textColour : backColour;
      myScreen.dRectangle(x * w + x_offset, y * h + y_offset, w, h, color);
    }

    Serial.print("\n");
  }

  // 底部空区域
  Serial.print("\n\n\n\n");
}

///
/// @brief 请求JSON数据并进行绘制
///
void request_json_and_display() {
  // 检查网络连接
  if (Ethernet.linkStatus() == LinkON) {

    // 网络CLient
    EthernetClient client;

    Serial.print("[HTTP] begin...\n");
    Serial.println(API_URL);

    // 连接网络服务
    client.setTimeout(10000);
    if (!client.connect(API_URL_HOST, API_URL_PORT)) {
      Serial.println(F("Connection failed"));
      return;
    }

    // 请求API接口
    client.println(formatString("GET %s HTTP/1.0", API_URL_PATH));
    client.println(formatString("Host: %s", API_URL_HOST));
    client.println(F("Connection: close"));

    // 请求完毕
    if (client.println() == 0) {
      Serial.println(F("Failed to send request"));
      client.stop();
      return;
    } else {
      // HTTP
      Serial.print("[HTTP] GET...\n");

      // 检查返回状态
      char status[32] = { 0 };
      client.readBytesUntil('\r', status, sizeof(status));

      // 正确返回 "HTTP/1.0 200 OK" or "HTTP/1.1 200 OK"
      if (strcmp(status + 9, "200 OK") != 0) {
        Serial.print(F("Unexpected response: "));
        Serial.println(status);
        client.stop();
        return;
      }

      // 设置返回码
      int httpCode = 200;

      // 跳过 HTTP headers信息
      char endOfHeaders[] = "\r\n\r\n";
      if (!client.find(endOfHeaders)) {
        Serial.println(F("Invalid response"));
        client.stop();
        return;
      }

      // 检查返回码
      if (httpCode > 0) {
        // 输出返回码
        Serial.printf("[HTTP] GET... code: %d\n", httpCode);

        // 检查返回码
        if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
          // JsonDocument实例
          JsonDocument doc;

          // 解析为JSON
          DeserializationError error = deserializeJson(doc, client);
          if (error) {
            Serial.print(F("deserializeJson() failed: "));
            Serial.println(error.f_str());
            client.stop();
            return;
          } else {
            // 解析数据内容
            int x_offset = 0;
            int y_offset = 0;
            int orientation = 2;

            interval = doc["interval"].as<long>();
            x_offset = doc["offset"][0].as<long>();
            y_offset = doc["offset"][1].as<long>();
            orientation = doc["orientation"].as<long>();

            Serial.println(F("Response:"));
            Serial.println(formatString("name: %s", doc["name"].as<const char *>()));
            Serial.println(formatString("orientation: %d", orientation));
            Serial.println(formatString("offset: (%d,%d)", x_offset, y_offset));
            Serial.println(formatString("version: %0.2f", doc["version"].as<float>()));
            Serial.println(formatString("data size: %d", doc["data"].size()));

            // 设置默认方向
            myScreen.setOrientation(orientation);

            for (int i = 0; i < doc["data"].size(); i++) {
              String type = doc["data"][i]["type"];
              if (type == "comment") {
                // 注释
                String comment = doc["data"][i]["value"];
                Serial.println(formatString("comment: %s", comment.c_str()));
              } else if (type == "setOrientation") {
                // 设置方向
                int value = doc["data"][i]["value"].as<long>();
                Serial.println(formatString("setOrientation: %d", value));
                myScreen.setOrientation(value);
              } else if (type == "setOffset") {
                // 设置方向
                x_offset = doc["data"][i]["x"].as<long>();
                y_offset = doc["data"][i]["y"].as<long>();
                Serial.println(formatString("setOffset: (x, y)=(%d, %d)", x_offset, y_offset));
              } else if (type == "setPenSolid") {
                // 设置落笔抬笔
                int value = doc["data"][i]["value"].as<bool>();
                Serial.println(formatString("setPenSolid: %d", value));
                myScreen.setPenSolid(value);
              } else if (type == "selectFont") {
                // 设置字体
                String font_name = doc["data"][i]["value"];
                Serial.println(formatString("selectFont: %s", font_name.c_str()));
                uint8_t font = get_font(font_name);
                myScreen.selectFont(font);
              } else if (type == "setFontSolid") {
                // 设置字体落笔抬笔
                int value = doc["data"][i]["value"].as<bool>();
                Serial.println(formatString("setFontSolid: %d", value));
                myScreen.setFontSolid(value);
              } else if (type == "gText") {
                // 显示文本
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                String text = doc["data"][i]["text"];
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("gText: (x,y)=(%d,%d) text=%s color=%s", x, y, text.c_str(), color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.gText(x + x_offset, y + y_offset, text, color);
              } else if (type == "point") {
                // 画点
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("point: (x,y)=(%d,%d) color=%s", x, y, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.point(x + x_offset, y + y_offset, color);
              } else if (type == "line") {
                // 画线
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int x1 = doc["data"][i]["x1"].as<long>();
                int y1 = doc["data"][i]["y1"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("line: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.line(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, color);
              } else if (type == "rectangle") {
                // 画矩形
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int x1 = doc["data"][i]["x1"].as<long>();
                int y1 = doc["data"][i]["y1"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("rectangle: (x,y)=(%d,%d) (x1,y1)=(%d,%d) color=%s", x, y, x1, y1, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.rectangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, color);
              } else if (type == "circle") {
                // 画圆
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int r = doc["data"][i]["r"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("circle: (x,y)=(%d,%d) r=%d color=%s", x, y, r, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.circle(x + x_offset, y + y_offset, r, color);
              } else if (type == "triangle") {
                // 画三角形
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int x1 = doc["data"][i]["x1"].as<long>();
                int y1 = doc["data"][i]["y1"].as<long>();
                int x2 = doc["data"][i]["x2"].as<long>();
                int y2 = doc["data"][i]["y2"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("triangle: (x,y)=(%d,%d) (x1,y1)=(%d,%d) (x2,y2)=(%d,%d) color=%s", x, y, x1, y1, x2, y2, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.triangle(x + x_offset, y + y_offset, x1 + x_offset, y1 + y_offset, x2 + x_offset, y2 + y_offset, color);
              } else if (type == "dLine") {
                // 画线(向量)
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int w = doc["data"][i]["w"].as<long>();
                int h = doc["data"][i]["h"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("dLine: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.dLine(x + x_offset, y + y_offset, w, h, color);
              } else if (type == "dRectangle") {
                // 画矩形(向量)
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int w = doc["data"][i]["w"].as<long>();
                int h = doc["data"][i]["h"].as<long>();
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("dRectangle: (x,y)=(%d,%d) (w,h)=(%d,%d) color=%s", x, y, w, h, color_name.c_str()));
                uint16_t color = get_color(color_name);
                myScreen.dRectangle(x + x_offset, y + y_offset, w, h, color);
              } else if (type == "qrcode") {
                // 画二维码
                int x = doc["data"][i]["x"].as<long>();
                int y = doc["data"][i]["y"].as<long>();
                int w = doc["data"][i]["w"].as<long>();
                int h = doc["data"][i]["h"].as<long>();
                String text = doc["data"][i]["text"];
                String color_name = doc["data"][i]["color"];
                Serial.println(formatString("qrcode: (x,y)=(%d,%d) text=%s color=%s", x, y, text.c_str(), color_name.c_str()));
                uint16_t color = get_color(color_name);
                draw_qrcode(x + x_offset, y + y_offset, w, h, text, color);
              } else {
                Serial.println(formatString("type: %s", type));
              }
            }
            myScreen.flush();
          }
        }
      } else {
        // 输出错误信息
        Serial.printf("[HTTP] GET... failed, error: %d\n", httpCode);
      }

      // 结束请求
      client.stop();
    }
  }
}

void setup() {
  Serial.begin(115200);
  // Serial.setDebugOutput(true);

  Serial.println();
  Serial.println();
  Serial.println();

  wait(2);
  pinMode(LED_BUILTIN, OUTPUT);

  // 开始联网，LED点亮
  digitalWrite(LED_BUILTIN, HIGH);

  // 静态IP设置
  Serial.println("Ethernet Begin");
  Ethernet.init(17);
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  // 输出网卡mac地址、IP地址、子网掩码、DNS、网关
  Serial.print("My Mac address: ");
  byte macBuffer[6];               // 设置mac地址存储buff
  Ethernet.MACAddress(macBuffer);  // 读取mac地址
  for (byte octet = 0; octet < 6; octet++) {
    Serial.print(macBuffer[octet], HEX);
    if (octet < 5) {
      Serial.print('-');
    }
  }
  Serial.println("");

  Serial.print("My IP address: ");
  Serial.println(Ethernet.localIP());

  Serial.print("My subnet: ");
  Serial.println(Ethernet.subnetMask());

  Serial.print("My DNS address: ");
  Serial.println(Ethernet.dnsServerIP());

  Serial.print("My GateWay address: ");
  Serial.println(Ethernet.gatewayIP());

  Serial.println(" OK!");

  // 检查联网状态
  while (true) {
    if (Ethernet.linkStatus() == Unknown) {
      Serial.println("Link status unknown. Link status detection is only available with W5200 and W5500.");
    } else if (Ethernet.linkStatus() == LinkON) {
      Serial.println("Link status: On");
      break;
    } else if (Ethernet.linkStatus() == LinkOFF) {
      Serial.println("Link status: Off");
    }
    wait(1);
  }

  // 联网成功，灯灭
  digitalWrite(LED_BUILTIN, LOW);


  // 屏幕初始化
  Serial.println("begin... ");
  myScreen.begin();
  Serial.println(formatString("%s %ix%i", myScreen.WhoAmI().c_str(), myScreen.screenSizeX(), myScreen.screenSizeY()));

  // 开始请求JSON数据，灯亮
  Serial.println("request json and display... ");
  digitalWrite(LED_BUILTIN, HIGH);

  // 清屏
  myScreen.clear();
  // 请求API接口获取数据并进行处理
  request_json_and_display();

  // 开始请求JSON数据，请求完毕，灯灭
  digitalWrite(LED_BUILTIN, LOW);

  Serial.println("=== ");
  Serial.println();

  now = millis();
}

void loop() {
  // 定时刷新检查
  if (millis() - now > interval * 1000) {
    refresh = true;
  }

  // BOOTSEL按键检查
  if (BOOTSEL) {
    refresh = true;

    // 按下，灯亮
    digitalWrite(LED_BUILTIN, HIGH);

    Serial.printf("\a\aYou pressed BOOTSEL!\n");
    // 等待BOOTSEL被释放
    while (BOOTSEL) {
      delay(1);
    }

    // 释放，灯灭
    digitalWrite(LED_BUILTIN, LOW);
    delay(100);
  }

  // 检查是否需要刷新数据
  if (refresh) {
    now = millis();
    refresh = false;

    // 开始请求JSON数据，灯亮
    Serial.println("request json and display... ");
    digitalWrite(LED_BUILTIN, HIGH);

    // 清屏
    myScreen.clear();
    // 请求API接口获取数据并进行处理
    request_json_and_display();

    // 开始请求JSON数据，请求完毕，灯灭
    digitalWrite(LED_BUILTIN, LOW);
  }
}
